
`ifndef __PSM6_SOC__
`define __PSM6_SOC__

`define PSM_ENABLE_INITIAL 
`define PSM_JP_NUM 2 // 不定义则使用异步RAM，适合xilinx  ，定义则适合quartus

/*-------------------------------------------------------------*\
1.带ROM的例化模板
psm6_soc #(
    .memfile("firmware.hex") ,  // 固件
    .memnum ( 4096)  ,           // 固件大小 <= 4096
	.stack_size  (4) ,           // 1 ~ 32 
	.scratch_size( 256),         // 1 ~ 256
    .interrupt_vector(12'h3ff)   // 中断入口地址
) psm_soc_u1 (
    .clk (clk) ,
    .reset(~rst_n ) ,
    .in_port (in_port ),
    .out_port(out_port) ,
    .port_id (port_id ),
    .write_strobe(write_strobe) ,
    .interrupt(interrupt) ,
    .interrupt_ack()
); 

2.单纯的psm6 例化模板
psm6  #(
	.stack_size(stack_size) ,
	.scratch_size(scratch_size),
    .interrupt_vector(12'h3ff)  // 中断向量地址 
)mcu(
    .clk               ( clk         ),   //系统时钟
    .reset             (  reset      ),   //复位  1 --> 复位
    .address           (  address    ),   //程序取址地址
    .instruction       (  instruction),   //指令输入 
    .in_port           ( in_port     ),   //输入口  
    .port_id           ( port_id     ),   //io口地址
    .out_port          ( out_port    ),   //输出口
    .write_strobe      ( write_strobe),   //输出口写使能  
    .read_strobe       ( ),   // 
    .interrupt         ( interrupt),   //中断触发信号
    .interrupt_ack     ( interrupt_ack)   //中断响应信号
);
\*-------------------------------------------------------------*/

module psm6_dpram (
	address_a,
	address_b,
	clock,
	data_a,
	data_b,
	wren_a,
	wren_b,
	q_a,
	q_b);

	input	[4:0]  address_a;
	input	[4:0]  address_b;
	input	  clock;
	input	[7:0]  data_a;
	input	[7:0]  data_b;
	input	  wren_a;
	input	  wren_b;
	output	 [7:0]  q_a;
	output	 [7:0]  q_b;

	(* ram_style="block" *)  reg [7:0 ] _mem [0:31]  /* synthesis syn_ramstyle = "block_ram" */; 
	always @(posedge clock) begin
		if(wren_a) 
			_mem[address_a] <= data_a ;
		else if (wren_b) 
			_mem[address_b] <= data_b ; 
	end	

`ifdef PSM_JP_NUM
	reg [7:0] q_a_r , q_b_r ;
	assign q_a = q_a_r ;
	assign q_b = q_b_r ;
	always @(posedge clock ) begin
		if(~wren_a)q_a_r <= _mem[address_a] ;
		if(~wren_b)q_b_r <= _mem[address_b] ;
	end
`else
	assign q_a = _mem[address_a] ;
	assign q_b = _mem[address_b] ;
`endif 
	
`ifdef PSM_ENABLE_INITIAL 
  integer i ; 
  initial begin 
    for(i=0;i<32;i=i+1)
      _mem[i] = 0 ;
  end
`endif 

endmodule

// psm6_register 
module psm6_register (
  input           clk,
  input           x_write_enable,
  input [4:0]     x_address,
  input [4:0]     y_address,
  input [7:0]     x_data_in,
  output [7:0]    x_data_out,
  output [7:0]    y_data_out
);

  psm6_dpram dpram (
    .address_a (x_address),
    .address_b (y_address),
    .clock     (clk),
    .data_a    (x_data_in),
    .data_b    (8'd0),
    .wren_a    (x_write_enable),
    .wren_b    (1'b0),
    .q_a       (x_data_out),
    .q_b       (y_data_out)
  );

endmodule

module psm6_idu(
    input wire  [17:0] instruction , 
    output wire [5:0]  opcode , 
    output wire [3:0] sX , 
    output wire [3:0] sY , 
    output wire [7:0] kk ,
    output wire [7:0] pp ,
    output wire [7:0] ss , 
    output wire [11:0] aaa
) ;

assign opcode = instruction[17:12] ;
assign sX = instruction[11:8] ;
assign sY = instruction[7:4]  ; 

assign kk = instruction[7:0]  ;
assign ss = instruction[7:0]  ;
assign pp = instruction[7:0]  ;
assign aaa= instruction[11:0]  ;

endmodule 


module psm6_dpram_2 #(
    parameter DATA_DEPTH = 256 ,  // 数据深度 ， 存的数据个数
    parameter DATA_WIDTH = 8   ,  // 数据位宽
    parameter ADDR_WIDTH = 8   ,  // 地址位宽
    parameter ENABLE_ASYNC   = 0      // 是否设置输出异步
) (
	address_a,
	address_b,
	clock,
	data_a,
	data_b,
	wren_a,
	wren_b,
	q_a,
	q_b);

	input	[ADDR_WIDTH - 1:0]  address_a;
	input	[ADDR_WIDTH - 1:0]  address_b;
	input	  clock;
	input	[DATA_WIDTH - 1:0]  data_a;
	input	[DATA_WIDTH - 1:0]  data_b;
	input	  wren_a;
	input	  wren_b;
	output	 [DATA_WIDTH - 1:0]  q_a;
	output	 [DATA_WIDTH - 1:0]  q_b;

	(* ram_style="block" *) reg [DATA_WIDTH - 1:0 ] _mem [0:DATA_DEPTH-1] /* synthesis syn_ramstyle = "block_ram" */; 

	always @(posedge clock) begin
		if(wren_a) 
			_mem[address_a] <= data_a ;
		else if (wren_b) 
			_mem[address_b] <= data_b ; 
	end	

generate
    genvar  i , j ;
	 reg [DATA_WIDTH - 1:0] q_a_r , q_b_r ;
	 
    if(|ENABLE_ASYNC) begin

        always @(*) begin
            q_a_r = (~wren_a) ? _mem[address_a]:q_a_r ;
            q_b_r = (~wren_b) ? _mem[address_b]:q_b_r ;
        end

    end else begin 
        
        always @(posedge clock ) begin
            if(~wren_a)q_a_r <= _mem[address_a] ;
            if(~wren_b)q_b_r <= _mem[address_b] ;
        end

    end 
	 
	for(i=0;i<DATA_WIDTH;i=i+1) begin :out_blk
       assign q_a[i] = q_a_r[i] ;
       assign q_b[i] = q_a_r[i] ;
   end
endgenerate
endmodule

module psm6_stack_ram #(
    parameter DATA_DEPTH = 8,
    parameter DATA_WIDTH = 8,
    parameter ADDR_WIDTH = 5 
)
(
    input clk, // 输入时钟信号
    input reset, // 输入复位信号
    input push, // 入栈信号
    input pop , 
    input [DATA_WIDTH - 1 : 0 ] i_data , 
    output [DATA_WIDTH - 1 :0] o_data // 输出栈顶信号
);
reg [ADDR_WIDTH -1 : 0 ] top ;
wire [ADDR_WIDTH -1 : 0 ] address ; 
assign address = top + push - pop;

reg [ DATA_WIDTH - 1 : 0 ] data_a ;
reg wren_a ;
always@(posedge clk) begin
    if(reset) begin 
        top <= {(ADDR_WIDTH){1'b1}}  ;
    end else 
    begin
        wren_a <= 1'b0;
        if(push) begin
            top <=  top + 1'b1;
            wren_a <= 1'b1;
            
        end 
   
        if( pop ) begin
            top <=  top - 1'b1  ;
        end
    end
end

always@(posedge clk) begin
  data_a <= i_data ;
end
  
psm6_dpram_2 #(
    .DATA_DEPTH    ( DATA_DEPTH ) ,  // 数据深度 ， 存的数据个数
    .DATA_WIDTH    ( DATA_WIDTH    ),  // 数据位宽
    .ADDR_WIDTH    ( ADDR_WIDTH    ),  // 地址位宽
    .ENABLE_ASYNC  ( 0    )   // 是否设置输出异步 1：EDA会使用逻辑单元模拟  0: eda大概率会调用memory
) u_cbb_dpram (
	.address_a ( address) ,
	.address_b ( top) ,
	.clock     ( clk) ,
	.data_a    ( data_a) ,
	.data_b    ( {DATA_WIDTH{1'b0}}) ,
	.wren_a    ( wren_a) ,  // 高电平时写数据
	.wren_b    ( 1'b0) ,
	.q_a       ( ) ,
	.q_b       ( o_data) 
) ;
endmodule

module psm6_stack #(
	parameter stack_size = 32
)(
  input           clk,
  input           reset,
  input           write_enable,
  input           update_enable,
  input           push_pop,
  input [11:0]     data_in,
  output [11:0] data_out
);

wire update ; 
assign update = update_enable & write_enable;

psm6_stack_ram # (
    .DATA_DEPTH ( stack_size),
    .DATA_WIDTH ( 12),
    .ADDR_WIDTH ( 5) 
) u_Stack (
    .clk    (clk), // 输入时钟信号
    .reset  (reset), // 输入复位信号
    .push   (update &(push_pop)), // 入栈信号
    .pop    (update &(~push_pop)), 
    .i_data (data_in), 
    .o_data (data_out) // 输出栈顶信号
);
endmodule

// psm_scratch 
module psm6_scratch #(
  parameter  scratch_size = 256
) (
  input             clk,
  input             write_enable,
  input [7:0]       address,
  input [7:0]       data_in,
  output reg[7:0]   data_out
);

  // reg[7:0]  spr[255:0];
  (* ram_style = "block" *)
  reg[7:0]  spr[scratch_size-1 : 0 ]  /* synthesis syn_ramstyle = "block_ram" */ ;
  
  always@(posedge clk) begin
    if(write_enable)
      spr[address] <= data_in;  
  end
  
  always@(posedge clk) begin
    data_out <= spr[address];
  end
  
`ifdef PSM_ENABLE_INITIAL
  integer i;
  initial begin
    for(i=0; i<scratch_size; i=i+1) 
      spr[i] = 8'd0;
  end
`endif 

endmodule



module psm6 #(
  parameter scratch_size = 256 ,
  parameter stack_size  = 32 ,
  parameter interrupt_vector = 12'h3ff   // 中断向量地址 
)(
  input         clk,
  input         reset,
  input [17:0]  instruction,
  input         interrupt,
  input [7:0]   in_port,  
  output[11:0]  address,
  output reg    write_strobe,
  output reg    read_strobe,
  output[7:0]   port_id,
  output[7:0]   out_port,
  output reg    interrupt_ack
);

// idu 
wire [5:0]  opcode ; 
wire [3:0] sX ; 
wire [3:0] sY ; 
wire [7:0] kk ;
wire [7:0] pp ;
wire [7:0] ss ; 
wire [11:0] aaa ;
`ifdef PSM_JP_NUM
reg [`PSM_JP_NUM-1:0] beat = 2'b01 ; 
initial begin
    if(`PSM_JP_NUM<2) begin
        $error("`PSM_JP_NUM must >=2") ;
        $finish;
    end

    if(interrupt_vector>12'hfff) begin
        $error("`interrupt_vector must <= 12'hfff") ;
        $finish;
    end
end
`else 
reg [1:0] beat = 2'b01 ; 
`endif 
reg [11:0] pc/* synthesis syn_preserve=1 */;
wire [11:0] pc_next/* synthesis syn_preserve=1 */;

reg c_flag = 0  ;
reg z_flag = 0  ;
reg c_flag_saved = 0  ;
reg z_flag_saved = 0  ;
reg interrupt_enable = 0 ; 
reg interrupt_latch=1'b0;
reg zero_carry_write_enable ; 
reg [2:0]reset_latch ;
reg regbank = 1'b0;  // 寄存器back 
reg sX_write ;
wire [7:0]sX_din ;
wire [7:0] sX_dout ,sY_dout ;
wire [7:0] alu_result ;

wire is_sX_dout = (opcode>=6'd16 && opcode<=6'd31 && opcode!=6'd21) | 
(opcode>=6'd0&&opcode<=6'd15 ) |
(opcode>=6'd44&&opcode<=6'd47 )| 
(opcode==6'd30 ) |
(opcode==6'd31 );

wire is_sY_dout = 
(opcode==6'h00)|
(opcode==6'h16)|
(opcode==6'h02)|
(opcode==6'h04)|
(opcode==6'h06)|
(opcode==6'h10)|
(opcode==6'h12)|
(opcode==6'h18)|
(opcode==6'h1A)|
(opcode==6'h0C)|
(opcode==6'h0E)|
(opcode==6'h1C)|
(opcode==6'h1E);

wire is_fetch = ((opcode==6'h0A)|(opcode==6'h0B)) ? 1'B1 : 1'B0 ;

wire is_sX_din_from_addalu =  (opcode==6'h10|opcode==6'h11 | opcode==6'h12|opcode==6'h13|
opcode==6'h18|opcode==6'h19 |opcode==6'h1a|opcode==6'h1b |
opcode==6'h1c|opcode==6'h1d|opcode==6'h1e|opcode==6'h1f ) ;

wire is_logic_alu = (opcode==6'h02 || opcode==6'h03 ||  opcode==6'h0c ||  opcode==6'h0d ||
  opcode==6'h0e || opcode==6'h0f || opcode==6'h04 || opcode==6'h05 || opcode==6'h06 || opcode==6'h07) ;

wire is_shift_alu = (opcode==6'h14) ;

assign port_id = (opcode == 6'h08 ||  opcode == 6'h2C) ? sY_dout : 
(opcode == 6'h09 ||  opcode == 6'h2D) ? pp : {4'h0 , pp[3:0]} ;

assign out_port = (opcode == 6'h2B) ? {sX,sY} : 
    sX_dout ;

wire [7:0] op1 = is_sX_dout ? sX_dout : kk ;
wire [7:0] op2 = is_sY_dout ? sY_dout : kk ;

wire c_add  ;
wire [7:0] add_alu_result ;
wire c_logic  ;
wire [7:0] logic_alu_result ;
wire c_shift  ;
wire [7:0] shift_alu_result ;

assign {c_add , add_alu_result} =  add_alu(op1,op2,c_flag , (opcode==6'h10||opcode==6'h11)?2'b00 :
(opcode==6'h12||opcode==6'h13)?2'b01 :
(opcode==6'h18||opcode==6'h19 || opcode==6'h1c|| opcode==6'h1d )?2'b10:2'b11)  ;


assign { c_logic, logic_alu_result} = logic_alu(op1,op2,c_flag,opcode) ;
assign { c_shift, shift_alu_result} = shift_alu(kk[3:0],op1 , c_flag) ; 

assign sX_din = (opcode == 6'h08 ||  opcode == 6'h09) ? in_port :
    is_fetch ? scratch_data_out : 
    is_shift_alu ? shift_alu_result : 
    is_sX_din_from_addalu? add_alu_result :
    is_logic_alu ? logic_alu_result : 
    (opcode==6'h00) ? sY_dout :
    (is_star&&(opcode==6'h16))? sY_dout :  kk;


// always @(*) begin
//     casez(opcode)
//         6'h08, 6'h09: sX_din = in_port;
//         6'h0A, 6'h0B: sX_din = scratch_data_out;
//         6'h14:        sX_din = shift_alu_result;
//         6'h10, 6'h11, 6'h12, 6'h13,
//         6'h18, 6'h19, 6'h1A, 6'h1B,
//         6'h1C, 6'h1D, 6'h1E, 6'h1F: sX_din = add_alu_result;
//         6'h02, 6'h03, 6'h04, 6'h05,
//         6'h06, 6'h07, 6'h0C, 6'h0D,
//         6'h0E, 6'h0F: sX_din = logic_alu_result;
//         6'h16 : begin 
//             if(is_star) sX_din = sY_dout;
//             else sX_din = kk ;
//         end
//         6'h00:        sX_din = sY_dout;
//         default:      sX_din = kk;
//     endcase
// end


wire c_flag_w =  is_shift_alu ? c_shift :
    is_sX_din_from_addalu ? c_add :
    is_logic_alu ? c_logic : c_flag; 

wire z_flag_w = (~|sX_din) & 
    ( 
        ((opcode==6'h12)|(opcode==6'h13) |(opcode==6'h1a)|(opcode==6'h1b)|(opcode==6'h0e)|(opcode==6'h0f)|(opcode==6'h1e)|(opcode==6'h1f)) 
        ? z_flag : 1'b1
    ) ; 


// reg z_flag_w;
// always@(*) begin
//     casez(opcode)
//         6'h12, 6'h13, 6'h1a, 6'h1b, 6'h0e, 6'h0f, 6'h1e, 6'h1f:
//             z_flag_w = (~|sX_din) & z_flag;
//         default:
//             z_flag_w = (~|sX_din) & 1'b1;
//     endcase
// end

wire is_jump ; 
wire [11:0] jump_address ;
assign {is_jump , jump_address } = jump_addr(
    opcode , 
    c_flag, 
    z_flag, 
    aaa , 
    sX_dout , 
    sY_dout
); 

wire is_call ; 
wire [11:0] call_address ;
assign {is_call , call_address } = call_addr(
    opcode , 
    c_flag, 
    z_flag, 
    aaa , 
    sX_dout , 
    sY_dout
); 

wire is_return    = opcode == 6'h25 ; 
wire is_returni   = opcode == 6'h29 ;
wire is_return_z  = (opcode == 6'h31) & z_flag  ;
wire is_return_nz = (opcode == 6'h35) & ~z_flag ;
wire is_return_c  = (opcode == 6'h39) & c_flag  ;
wire is_return_nc = (opcode == 6'h3d) & ~c_flag ;
wire is_load_return= opcode == 6'h21 ;

wire is_star = (opcode==6'h16)||(opcode==6'h17) ;
wire internal_reset ; 

assign pc_next = interrupt_latch ? interrupt_vector : // 中断
    is_jump ? jump_address :
    is_call ? call_address : 
    (is_return|is_return_z|is_return_nz|is_return_c|is_return_nc|is_load_return)? stack_data_out + 1'b1:
    is_returni ? stack_data_out:
    pc + 1'b1;


assign address = pc ;

wire interrupt_assert = interrupt & interrupt_enable ;

//Instruction Stack Ports
wire        stack_write_enable, stack_update_enable;
wire        stack_push_pop;
wire[11:0]   stack_data_in;
wire[11:0]   stack_data_out;

wire beat_act;

`ifdef PSM_JP_NUM
  assign stack_write_enable = internal_reset || beat[1];
  assign beat_act = beat[1] ;
`else 
  assign stack_write_enable = internal_reset || beat[1];
  assign beat_act = beat[1] ;
`endif 


assign stack_update_enable = beat_act && (is_call || is_return||is_return_z ||is_return_nz ||is_return_c ||is_return_nc || is_returni || interrupt_latch||is_load_return) ;        
assign stack_push_pop = interrupt_latch ||  ~(is_return||is_return_z ||is_return_nz ||is_return_c ||is_return_nc || is_returni||is_load_return)   ;

assign stack_data_in = pc;                                                                           
wire [7:0] scratch_address = ((opcode==6'h2e)|(opcode==6'h0a)) ? sY_dout : ss;
wire [7:0] scratch_data_in = sX_dout;

wire [7:0] scratch_data_out ;
reg scratch_write_enable =1'b0; 
psm6_scratch #(.scratch_size(scratch_size)) psm_scratch (
    .clk              (clk),
    .write_enable     (scratch_write_enable),
    .address          (scratch_address),
    .data_in          (scratch_data_in),
    .data_out         (scratch_data_out)
  );

psm6_idu  u_psm6_idu(
    .instruction(instruction) , 
    .opcode(opcode) , 
    .sX (sX ), 
    .sY (sY ), 
    .kk (kk ),
    .pp (pp ),
    .ss (ss ), 
    .aaa(aaa)
) ; 

psm6_register psm6_register (
    .clk              (clk),
    .x_address        ({is_star ? ~regbank : regbank, sX}),
    .y_address        ({regbank, sY}),
    .x_data_in        (sX_din),
    .x_write_enable   (sX_write),
    .x_data_out       (sX_dout),
    .y_data_out       (sY_dout)
  );  


psm6_stack #(.stack_size(stack_size)) psm6_stack (
    .clk              (clk),
    .reset            (reset),
    .write_enable     (stack_write_enable),
    .update_enable    (stack_update_enable),
    .push_pop         (stack_push_pop),
    .data_in          (stack_data_in),
    .data_out         (stack_data_out)
  );

always@(posedge clk) begin
  if(reset)
    reset_latch <= 3'b111;
  else
    reset_latch <= {reset_latch[1:0], 1'b0};
end
assign internal_reset = reset_latch[2];

always @(posedge clk ) begin
    sX_write <= 1'b0 ;
    read_strobe <= 1'b0;
    write_strobe <= 1'b0 ;
    zero_carry_write_enable <= 1'b0 ;
    scratch_write_enable <= 1'b0 ;
    interrupt_ack <= 1'b0 ;

    if(internal_reset) begin
`ifdef PSM_JP_NUM
        beat <= {{(`PSM_JP_NUM-1){1'b0}} , 1'b1 };
`else
		beat <= 2'b01;
`endif 
        sX_write <= 1'b0 ;
        pc <= 12'h00 ;
        interrupt_enable <= 1'b0 ;
        z_flag <= 1'b0 ;
        c_flag <= 1'b0;
        interrupt_latch<= 1'b0 ;
        zero_carry_write_enable <= 1'b0 ;
        scratch_write_enable <= 1'b0 ;
        regbank <= 1'b0; 
    end else begin 
`ifdef PSM_JP_NUM
        beat <= {beat[`PSM_JP_NUM-2:0], beat[`PSM_JP_NUM-1]};
`else
		beat <= {beat[0], beat[1]};
`endif 
        interrupt_latch <= interrupt_assert | interrupt_latch; 

        if(zero_carry_write_enable) begin
            z_flag <= z_flag_w ;
            c_flag <= c_flag_w ;
        end

        if(beat[1]) begin 
            pc <= pc_next ;
            if(interrupt_latch) begin 
                interrupt_enable <= 1'b0; // 自动关闭中断使能 
                interrupt_latch <= 1'b0 ;
                interrupt_ack <= 1'b1;
                z_flag_saved <= z_flag ;
                c_flag_saved <= c_flag ;
            end else begin
                sX_write <= (opcode>=6'h10&&opcode<=6'h14) | 
                    (opcode>=6'h16&&opcode<=6'h1B) | 
                    (opcode==6'h21) | 
                    (opcode>=6'h00&&opcode<=6'h0B) ;

                case(opcode)
                    6'h02,6'h03,6'h04,6'h05,6'h06,6'h07,6'h10,6'h11,6'h12,6'h13,6'h18,6'h19,6'h1A,6'h1B,6'h0C,
                    6'h0D,6'h0E,6'h0F,6'h1C,6'h1D,6'h1E,6'h1F,6'h14: zero_carry_write_enable <= 1'b1;
                    default:zero_carry_write_enable <= 1'b0;
                endcase

                case(opcode)
                    6'H29:begin // returni 
                        c_flag <= c_flag_saved ; 
                        z_flag <= z_flag_saved ;
                        interrupt_enable <= kk[0] ;
                    end
                    6'h28,6'h29:begin // 是否使能中断
                        interrupt_enable <= kk[0] ;
                    end
                    6'h08,6'h09:begin // input 
                        read_strobe <= 1'b1 ;
                    end
                    6'h2c,6'h2d,6'h2b:begin // output 
                        write_strobe <= 1'b1 ;
                    end
                    6'h2e,6'h2f:begin
                        scratch_write_enable <= 1'b1 ;
                    end
                    6'h37:begin  // 设置 regbank
                        regbank <= kk[0] ; 
                    end
                endcase 
            end
        end 
    end 
end

function [8:0] add_alu( input [7:0] op1 , input [7:0] op2 , input cin ,  input [1:0] op_type ) ;
reg [7:0] addsub ; 
reg addsub_carry ;
reg [8:0] addsub_result ;
reg [7:0] addsub_b ;
begin
    add_alu[8] = 1'b0 ;
    if(op_type == 2'b01) begin // addcy 
        addsub_carry = cin ;
    end else if(op_type == 2'b10) begin   // sub
        addsub_carry = 1'b1;
    end else if(op_type == 2'b11) begin   // subcy 
        addsub_carry = ~cin ; 
    end else 
        addsub_carry = 1'b0 ;   // add 

    addsub_b = ((op_type == 2'b10) || (op_type == 2'b11)) ? ~op2 : op2 ;
    addsub_result = op1 + addsub_b + addsub_carry ;

    if((op_type == 2'b01) || (op_type == 2'b00)) begin // addcy  add 
        add_alu[8] = addsub_result[8] ;
    end  else 
        add_alu[8] = ~addsub_result[8] ;  

    add_alu[7:0] = addsub_result[7:0] ;
end
endfunction 

function [8:0] logic_alu(
    input [7:0] op1,
    input [7:0] op2,
    input cin ,
    input [5:0] op 
) ;
begin
    logic_alu[8] = 1'b0 ;
    if(op==6'h02 || op==6'h03 ||  op==6'h0c ||  op==6'h0d ||  op==6'h0e || op==6'h0f) begin // and 
        logic_alu[7:0] = op1 & op2 ;
    end else if( op==6'h04 || op==6'h05) begin
        logic_alu[7:0] = op1 | op2 ;
    end else begin
        logic_alu[7:0] = op1 ^ op2 ;
    end

    if(op == 6'h0c || op == 6'h0d) begin
        logic_alu[8] = ^logic_alu[7:0] ;
    end else if(op == 6'h0e || op == 6'h0f) begin
        logic_alu[8] = ^{cin,logic_alu[7:0]} ;
    end
end
endfunction 

function [8:0] shift_alu(
    input [3:0] shift_op ,
    input [7:0] op1 ,
    input cin
) ; 

reg shift_bit;
reg shift_dir;
begin 
    shift_dir = 1'b0 ;
    shift_bit = 1'b0 ; 
    case(shift_op)
        4'h6:begin
            shift_bit = 1'b0;
        end
        4'h7:begin
            shift_bit = 1'b1;
        end
        4'h4:begin
            shift_bit = op1[0] ;
        end
        4'h0:begin
            shift_bit = cin ;
        end

        4'h2:begin  //RL 
            shift_bit = op1[7] ;
        end

        4'hE:begin  // SR0
            shift_bit = 1'B0;
            shift_dir= 1'b1;
        end
        4'hF:begin  // SR1 
            shift_bit = 1'B1 ;
            shift_dir= 1'b1;
        end
        4'hA:begin  // SR1 
            shift_bit = op1[7] ;
            shift_dir= 1'b1;
        end
        4'h8:begin  // SR1 
            shift_bit = cin ;
            shift_dir= 1'b1;
        end
        4'hc:begin  // RR
            shift_bit = op1[0] ;
            shift_dir= 1'b1;
        end

        default: shift_bit=1'b0 ;
    endcase 

    shift_alu[7:0] = shift_dir==1'b0 ? {op1[6:0],shift_bit} : {shift_bit, op1[7:1]} ;
    shift_alu[8]  = shift_dir==1'b0  ? op1[7] : op1[0] ;

end 
endfunction 

function [12:0]jump_addr(
    input [5:0]op , 
    input cin, 
    input zin , 
    input [11:0] aaa , 
    input [7:0]sX_dout , 
    input [7:0]sY_dout
);
    begin
        jump_addr[12] = 1'b0 ;
        case(op) 
            6'h22:begin
                jump_addr[12] = 1'b1 ;
            end
            6'h32:begin
                if(zin)jump_addr[12] = 1'b1 ;
            end
            6'h36:begin
                if(~zin)jump_addr[12] = 1'b1 ;
            end
            6'h3a:begin
                if(cin)jump_addr[12] = 1'b1 ;
            end
            6'h3e:begin
                if(~cin)jump_addr[12] = 1'b1 ;
            end
            6'h26:begin
                jump_addr[12] = 1'b1 ;
            end
        endcase
        jump_addr[11:0] = op==6'h26 ? {sX_dout[3:0] , sY_dout}  : aaa;
    end
endfunction

function [12:0]call_addr(
    input [5:0]op , 
    input cin, 
    input zin , 
    input [11:0] aaa , 
    input [7:0]sX_dout , 
    input [7:0]sY_dout
);
    begin
        call_addr[12] = 1'b0 ;
        case(op) 
            6'h20:begin
                call_addr[12] = 1'b1 ;
            end
            6'h30:begin
                if(zin)call_addr[12] = 1'b1 ;
            end
            6'h34:begin
                if(~zin)call_addr[12] = 1'b1 ;
            end
            6'h38:begin
                if(cin)call_addr[12] = 1'b1 ;
            end
            6'h3c:begin
                if(~cin)call_addr[12] = 1'b1 ;
            end
            6'h24:begin
                call_addr[12] = 1'b1 ;
            end
        endcase
        call_addr[11:0] = op==6'h24 ? {sX_dout[3:0] , sY_dout}  : aaa;
    end
endfunction
endmodule 

// soc 调用
module psm6_soc #(
    parameter memfile = "firmware.hex" , 
    parameter memnum  =  4096  , 
	parameter stack_size  = 32 ,  // 1 ~ 32 
	parameter scratch_size = 256, // 1 ~ 256  
    parameter interrupt_vector = 12'hfff   // 中断向量地址 , psm3为3ff 
) (
    input clk  ,
    input reset ,
    input  wire [7:0]  in_port ,
    output wire [7:0]  out_port ,
    output wire [7:0]  port_id,
    output wire        write_strobe ,
    input  wire interrupt ,
    output wire interrupt_ack
); 

function integer log2(input integer d); for(log2=0;|d;log2=log2+1) d=d>>1 ; endfunction 

(* ram_style = "block" *)
reg [17 : 0] mem [0 : memnum-1] /* synthesis syn_ramstyle = "block_ram" */; 
reg [17:0] instruction /* synthesis syn_preserve=1 */ ;
wire [11:0] address /* synthesis syn_preserve=1 */ ;

initial begin 
    if(|memfile) begin 
        $readmemh(memfile , mem ) ; 
        $display("Preloading %m from %s", memfile);
    end  

    if(memnum > 4096) begin
        $error("memnum must <= 4096") ;
        $stop;
    end
end 

always @(posedge clk) begin 
	if(~reset)instruction <= mem[address];
end 

psm6  #(
	.stack_size(stack_size) ,
	.scratch_size(scratch_size),
    .interrupt_vector(interrupt_vector)  // 中断向量地址 
)mcu(
    .clk               ( clk         ),   //系统时钟
    .reset             (  reset      ),   //复位  1 --> 复位
    .address           (  address    ),   //程序取址地址
    .instruction       (  instruction),   //指令输入 
    .in_port           ( in_port     ),   //输入口  
    .port_id           ( port_id     ),   //io口地址
    .out_port          ( out_port    ),   //输出口
    .write_strobe      ( write_strobe),   //输出口写使能  
    .read_strobe       ( ),   // 
    .interrupt         ( interrupt),   //中断触发信号
    .interrupt_ack     ( interrupt_ack)   //中断响应信号
);
endmodule

`endif 
